package org.jw.wishlist.resolver;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static org.apache.commons.lang3.ObjectUtils.isNotEmpty;

import cn.hutool.core.text.UnicodeUtil;
import cn.hutool.core.util.EscapeUtil;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.collect.Lists;
import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.jw.wishlist.entity.source.ItemSource;
import org.jw.wishlist.resolver.TaobaoItemSourceResolver.ThirdPartyItemSourceResolver.Response.Item;
import org.springframework.lang.Nullable;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

/**
 * 解析淘宝商品
 *
 * @author Jiawei Zhao
 */
@Slf4j
public class TaobaoItemSourceResolver extends ItemSourceSupports implements ItemSourceResolver {

  private static final String SOURCE_KEY = "taobao";
  private final List<ItemSourceResolver> resolvers;
  private String id;
  private String shortUrl;

  public TaobaoItemSourceResolver(RestTemplate restTemplate) {
    super(restTemplate);
    this.resolvers = Lists.newArrayList(
        new HtmlItemSourceResolver(),
        new ThirdPartyItemSourceResolver()
    );
  }

  @Override
  public ItemSource resolve(String linkCode) {
    for (ItemSourceResolver resolver : resolvers) {
      try {
        return resolver.resolve(linkCode);
      } catch (Exception ignored) {
        // ignored
      }
    }
    throw new IllegalArgumentException("解析失败");
  }

  class HtmlItemSourceResolver implements ItemSourceResolver {

    private static final Pattern LONG_URL_PATTERN = Pattern.compile("var url = 'http\\S+'");
    private static final Pattern PRICE_PATTERN = Pattern.compile("<em class=\"tb-rmb-num\">.+</em></strong>");

    private static String parseLongUrl(String shortUrlResponse) {
      Matcher longUrlMatcher = LONG_URL_PATTERN.matcher(shortUrlResponse);
      checkState(longUrlMatcher.find(), "商品链接解析失败");
      String longUrl = shortUrlResponse.substring(longUrlMatcher.start() + 11, longUrlMatcher.end() - 1);
      log.info("商品链接解析结果: {}", longUrl);
      return longUrl;
    }

    @Nullable
    private static String tryParsePrice(String longUrlResponse) {
      Matcher priceMatcher = PRICE_PATTERN.matcher(longUrlResponse);
      if (priceMatcher.find()) {
        String price = longUrlResponse.substring(priceMatcher.start() + 23, priceMatcher.end() - 14).trim();
        log.info("价格解析结果: {}", price);
        return price;
      }

      return null;
    }

    private static String getProperty(String name, String source) {
      Pattern titlePattern = Pattern.compile(name + ".+,");
      Matcher titleMatcher = titlePattern.matcher(source);
      checkArgument(titleMatcher.find(), "属性解析失败: %s", name);
      String property = source.substring(titleMatcher.start(), titleMatcher.end() - 2);
      return property.substring(property.indexOf(":") + 1).trim().substring(1);
    }

    private static String titleToString(String value) {
      String result = UnicodeUtil.toString(value);
      result = EscapeUtil.unescapeHtml4(result);
      return result.replace("\\", "");
    }

    @Override
    public ItemSource resolve(String linkCode) {
      shortUrl = extractUrl(linkCode);
      String shortUrlResponse = restTemplate.getForEntity(URI.create(shortUrl), String.class).getBody();
      checkState(isNotEmpty(shortUrlResponse), "短链接请求返回为空");

      String longUrl = parseLongUrl(shortUrlResponse);
      id = extractPropertyFromQueryString(longUrl, "id", String.class)
          .orElseThrow(() -> new IllegalArgumentException("解析失败"));

      String longUrlResponse = restTemplate.getForEntity(URI.create(longUrl), String.class).getBody();
      checkState(isNotEmpty(longUrlResponse), "长链接商品请求返回为空");

      String title = titleToString(getProperty("title", longUrlResponse));
      log.info("商品标题解析结果: {}", title);

      String pic = "https://" + getProperty("pic", longUrlResponse).substring(2);
      log.info("商品图片地址解析结果: {}", pic);

      String price = tryParsePrice(longUrlResponse);

      return new ProductItemSourceBuilder()
          .itemId(id)
          .key(SOURCE_KEY)
          .linkCode(linkCode)
          .name(title)
          .originUrl(shortUrl)
          .pictureUrl(pic)
          .price(price)
          .build();
    }

  }

  class ThirdPartyItemSourceResolver implements ItemSourceResolver {

    private static final String KEY = "t5018766811";
    private static final String SECRET = "20230322";
    private static final String URL_TEMPLATE = "https://api-gw.onebound.cn/taobao/item_get/?key={key}&num_iid={itemId}&is_promotion=0&lang=zh-CN&secret={secret}";
    private static final String PARSE_FAILED = "解析失败";

    @Override
    public ItemSource resolve(String linkCode) {

      checkArgument(id != null && isNotEmpty(shortUrl), PARSE_FAILED);

      Map<String, String> variables = new HashMap<>();
      variables.put("key", KEY);
      variables.put("itemId", id.toString());
      variables.put("secret", SECRET);

      URI uri = UriComponentsBuilder.fromUriString(URL_TEMPLATE)
          .buildAndExpand(variables)
          .toUri();

      Item item = Optional.ofNullable(restTemplate.getForEntity(uri, Response.class).getBody())
          .map(Response::getItem)
          .orElseThrow(() -> new IllegalArgumentException(PARSE_FAILED));

      checkArgument(isNotEmpty(item.getTitle()), PARSE_FAILED);
      checkArgument(isNotEmpty(item.getPicUrl()), PARSE_FAILED);

      return new ProductItemSourceBuilder()
          .itemId(id)
          .key(SOURCE_KEY)
          .linkCode(linkCode)
          .name(item.getTitle())
          .originUrl(shortUrl)
          .pictureUrl(item.getPicUrl())
          .price(item.getPrice())
          .build();
    }

    @Data
    public static class Response {

      private Item item;

      @Data
      public static class Item {

        private String title;

        @JsonProperty("orginal_price")
        private String price;

        @JsonProperty("pic_url")
        private String picUrl;

      }

    }

  }

}


